using System;
using System.IO;
using System.Xml;
using System.Data;
using System.Text;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Generic;

namespace ReleaseIt
{
    public partial class MainForm : Form
    {
        List<string> copiedFiles = new List<string>();
		List<string> errorList = new List<string>();
		
		List<Profile> profiles = new List<Profile>();
		string activeProfilesFile = "default.riprofiles";
		
        public MainForm()
        {
            InitializeComponent();

			// Load profiles
			try
			{
				// Load profile from disk
				LoadProfiles(activeProfilesFile);
			}
			catch
			{
				// Read failed - go with the built-in
				LoadHardWiredProfiles();
				errorList.Add("Could not load profiles from disk");
			}

			// Select radio buttons
			sourceDirectoryRadioButton.Checked = true;
			destinationDirectoryRadioButton.Checked = true;
			
            // Disable unused controls
            sourceProjectRadioButton.Enabled = false;
            ftpComboBox.Enabled = false;
            ftpRadioButton.Enabled = false;
            zipRadioButton.Enabled = false;
            zipTextBox.Enabled = false;
            zipButton.Enabled = false;
			profileConfirugrationToolStripMenuItem.Enabled = false;
			viewLogFileToolStripMenuItem.Enabled = false;
			exceptionsToolStripMenuItem.Enabled = false;
			configureToolStripMenuItem.Enabled = false;
			submitBugToolStripMenuItem.Enabled = false;
			checkForUpdateToolStripMenuItem.Enabled = false;
			
			// Sync form to profiles
			SyncFormToProfiles();
        }

		private void WriteProfiles(string filename)
		{
			Stream fileStream = new FileStream(filename, FileMode.Create);
			
			XmlTextWriter writer = new XmlTextWriter(fileStream, System.Text.Encoding.ASCII);
				
      		// Write the <xml version="1.0"> element
      		writer.WriteStartDocument();

      		// Write the root element
     		writer.WriteStartElement("ProfilesDataSet");
			writer.WriteAttributeString("xmlns", "http://tempuri.org/ProfilesDataSet.xsd");

			for (int index=0; index < profiles.Count; index++)
			{
				Profile profile = profiles[index];
				string profileID = (index + 1).ToString();
				
				// Write the configuration profile
				writer.WriteStartElement("Profile");
				writer.WriteElementString("ProfileID", profileID);
				writer.WriteElementString("ProfileName", profile.Name);
				writer.WriteElementString("IsDefaultProfile", "true");
				writer.WriteElementString("CopyEmptyDirectories", "false");
				writer.WriteElementString("AutoFillLastUsed", "true");
				writer.WriteElementString("BackupFiles", "false");
				writer.WriteElementString("BackupFilesToTemporary", "false");
				writer.WriteElementString("BackupFilesCreateDirectories", "true");
				writer.WriteElementString("BackupFilesDirectory","");
				writer.WriteElementString("OverwriteTarget", "true");
				writer.WriteElementString("ShowFilesToCopy", "false");
				writer.WriteElementString("ShowFoldersToInclude", "false");
				writer.WriteElementString("EnableLogging", "false");
				writer.WriteElementString("LogFile", "");
				writer.WriteElementString("LastUsedSource", "1"); // 1 = Directory?
				writer.WriteElementString("LastUsedSourceDir", profile.SourceDirectory);
				writer.WriteElementString("LastUsedSourceProject", "");
				writer.WriteElementString("LastUsedSourceConfig", "");
				writer.WriteElementString("LastUsedDestination", "1"); // 1 = Index of something
				writer.WriteElementString("LastUsedDestinationDir", profile.DestinationDirectory);
				writer.WriteElementString("LastUsedFtpProfile", ftpComboBox.SelectedIndex.ToString());
				writer.WriteElementString("LastUsedZipFileName", zipTextBox.Text);
				writer.WriteElementString("PreDeployCommand", "");
				writer.WriteElementString("PreDeployArguments", "");
				writer.WriteElementString("PostDeployCommand", "");
				writer.WriteElementString("PostDeployArguments", "");
				writer.WriteEndElement();

				// Write the list of file masks
				foreach (Mask mask in profile.FileMasks)
				{				
					// Write the XML
            		writer.WriteStartElement("FileMask");
					writer.WriteElementString("ProfileID", profileID);
             		writer.WriteElementString("Mask", mask.Text);
					writer.WriteElementString("IsSelected", mask.IsSelected.ToString());
             		writer.WriteEndElement();
				}
			}

      		//Close the root element - end of XML
      		writer.WriteEndElement();

			// Wrap-it up
      		writer.Flush();
      		writer.Close();
      		fileStream.Close();
		}

		private void LoadProfiles(string filename)
		{
			// Empty the list of profiles
			profiles.Clear();

			// Open the XML file that contains the profiles
			Stream filestream = new FileStream(filename, FileMode.Open);
			XmlTextReader reader = new XmlTextReader(filestream);

      		reader.WhitespaceHandling = WhitespaceHandling.None;

			if (filestream.Length != 0)
			{
				reader.ReadStartElement();

				int pIndex = 0;
				
				while (!reader.LocalName.Equals("ProfilesDataSet"))
				{
					switch (reader.LocalName)
					{
					case "Profile":
						reader.Read();  // Move past <Profile>
						
						while (!reader.LocalName.Equals("Profile"))
						{
							
							string node = reader.LocalName;
							string data = reader.ReadString();

							switch(node)
							{
							case "ProfileID":
								pIndex = Convert.ToInt32(data) - 1;
								//Console.WriteLine("Reading profile #" + data);
								Profile p = new Profile();
								// ***** Nasty assumption here *****
								// ***** If profiles are not seqential ***
								// ***** in the XML file this will cause problems ****
								profiles.Add(p);
								break;
							case "ProfileName":
								profiles[pIndex].Name = data;
								break;
							case "LastUsedSourceDir":
								profiles[pIndex].SourceDirectory = data;
								break;
							case "LastUsedDestinationDir":
								profiles[pIndex].DestinationDirectory = data;
								break;
							case "LastUsedSource":
								// What source should be we use for files
								// Right now we only do directory so enable that
								//sourceDirectoryRadioButton.Checked = true;
								break;
							case "LastUsedDestination":
								// Where should files go?
								// Right now we only do diretory so enable that
								//destinationDirectoryRadioButton.Checked = true;
								break;
							default:
								// Silently move past ignored <Profile> elements
								//Console.WriteLine("Ignored " + node + " -> " + data);
								break;
							}
							
							reader.Read();
						}
						reader.Read(); // Move past </Profile>

						break;
						
					case "FileMask":
						reader.Read(); // Move past <FileMask>

						string mask = "";
						bool isSelected = false;
						
						while (!reader.LocalName.Equals("FileMask"))
						{
							// Read XML info
							string node = reader.LocalName;
							string data = reader.ReadString();

							// Act on the info
							switch (node)
							{
							case "ProfileID":
								pIndex = Convert.ToInt32(data) - 1;
								break;
							case "Mask":
								mask = data;
								break;
							case "IsSelected":
								isSelected = Convert.ToBoolean(data);
								break;
							}

							// Advance to the next node
							reader.Read();
						}
						// Add the new file mask to the ListBox
						//int index = fileMaskCheckedListBox.Items.Add(mask);
						//fileMaskCheckedListBox.SetItemChecked(index, isSelected);
						Mask m = new Mask(mask, isSelected);
						profiles[pIndex].FileMasks.Add(m);

						// Move past </FileMask>
						reader.Read();
						break;

					case "FolderMask":
					reader.Read(); // Move past <FolderMask>

						string fm = "";
						bool folderSelected = false;
						
						while (!reader.LocalName.Equals("FolderMask"))
						{							
							// Read XML info
							string node = reader.LocalName;
							string data = reader.ReadString();

							// Act on the info
							switch (node)
							{
							case "ProfileID":
								break;
							case "Mask":
								fm = data;
								break;
							case "IsSelected":
								folderSelected = Convert.ToBoolean(data);
								break;
							}
							
							// Advance to the next node
							reader.Read();
						}
						// Deal with the FolderMask
						// **** TODO *********************
						// **** Deal with FolderMasks ****
						Console.WriteLine("FolderMask: " + fm + " (checked = " + folderSelected.ToString() + ") ignored");

						// Move past </FolderMask>
						reader.Read();
						break;
						
					default:
						string tag = reader.LocalName;

						Console.WriteLine("Unhandled XML: " + tag);
						reader.Read();
						break;
					}
				}
				//reader.Read();  // Advance past </ProfileDataSet>
			}
			reader.Close();
			filestream.Close();

			// Report our status
			ShowStatus("Loaded profiles from " + filename);
		}

        private void LoadHardWiredProfiles()
        {
			Profile activeProfile = new Profile("Quick Deploy Profile");
			profiles.Add(activeProfile);

			/*
			Profile other = new Profile("Another Profile");
			profiles.Add(other);
			other.FileMasks.Add("*.aspx");
			other.FileMasks.Add("*.xml");

            Profile another = new Profile();
            profiles.Add(another);
            */

            List<Mask> masks = activeProfile.FileMasks;

            // Set hard-wired file masks
			// They default to enabled (checked)
            masks.Add("*.asax");
            masks.Add("*.ascx");
            masks.Add("*.ashx");
            masks.Add("*.asmx");
            masks.Add("*.aspx");
            masks.Add("*.browser");
            masks.Add("*.config");
            masks.Add("*.css");
            masks.Add("*.dll");
            masks.Add("*.gif");
            masks.Add("*.htm");
            masks.Add("*.html");
            masks.Add("*.ico");
            masks.Add("*.jpg");
            masks.Add("*.js");
            masks.Add("*.master");
            masks.Add("*.png");
            masks.Add("*.resx");
            masks.Add("*.skin");
            masks.Add("*.theme");
            masks.Add("*.txt");
            masks.Add("*.xml");
            masks.Add("*.xsd");
            masks.Add("*.xsl");

			// Report status
			ShowStatus("Using built-in profile");
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void selectAllLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            for (int index=0; index<fileMaskCheckedListBox.Items.Count; index++)
            {
                fileMaskCheckedListBox.SetItemChecked(index, true);
            }
        }

        private void unselectLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            for (int index = 0; index < fileMaskCheckedListBox.Items.Count; index++)
            {
                fileMaskCheckedListBox.SetItemChecked(index, false);
            }
        }

        private void deployButton_Click(object sender, EventArgs e)
        {
			// Check to see if source directory has been supplied
            if (sourceDirectoryTextBox.Text.Length < 1)
			{
				MessageBox.Show("Please select a source directory", "Missing information");
			}
			else
            {
				// Check to see if target directory has been supplied
                if (destinationDirectoryTextBox.Text.Length < 1)
				{
					MessageBox.Show("Please select a destination directory", "Missing information");
				}
				else
                {
					// Retrieve the source and target directories
                    string sourceDir = sourceDirectoryTextBox.Text;
                    string destinationDir = destinationDirectoryTextBox.Text;

					// Empty the list of copied files
            		copiedFiles.Clear();

					// Create a list of file masks
					List<string> fileMasks = new List<string>();
					foreach (string mask in fileMaskCheckedListBox.CheckedItems)
						fileMasks.Add(mask);
					
					// Copy the files
					//CopyFiles(sourceDir, destinationDir);
					DeployDelegate deploy = new DeployDelegate(Deploy);
					deploy.BeginInvoke(sourceDir, destinationDir, fileMasks, null, null);
                }
            }
        }

		// Set the text that appears on statusLabel on statusStrip1
		private void UpdateStatusBar(string message)
		{
			statusLabel.Text = message;
		}

		delegate void UpdateStatusBarDelegate(string message);

		// Thread safe way to call UpdateStatusBar
		private void ShowStatus(string message)
		{
			if (statusStrip1.InvokeRequired == true)
			{
				// We are in a different thread than statusStrip1
				// Use a delegate to call UpdateStatusBar in the other thread
				UpdateStatusBarDelegate showStatus = new UpdateStatusBarDelegate(UpdateStatusBar);
				Invoke(showStatus, new object[] {message});
			}
			else
			{
				// We are in the same thread - just go ahead and call it
				UpdateStatusBar(message);
			}
		}

		private void DeploymentEnabled(bool toggle)
		{
			deployButton.Enabled = toggle;
			deployToolStripMenuItem.Enabled = toggle;
		}

		delegate void DeploymentEnabledDelegate(bool toogle);

		private void EnableDeployControls(bool toggle)
		{
			if (InvokeRequired == true)
			{
				DeploymentEnabledDelegate d = new DeploymentEnabledDelegate(DeploymentEnabled);
				Invoke(d, new object[] {toggle});
			}
			else
			{
				DeploymentEnabled(toggle);
			}
		}
		
		private void Deploy(string sourceDir, string destinationDir, List<string> masks)
		{
			// Disable the ability to start another deploy
			EnableDeployControls(false);
			
			// Announce that deployment is starting
			ShowStatus("Deployment in progress...");

			try // Try to deploy
			{
				// Loop once for each checked item in fileMaskCheckedListBox
           		foreach (string fileMask in masks)
            	{
            		// Copy files matching fileMask
                	RecursiveFileCopy(sourceDir, destinationDir, fileMask);
            	}

				// Announce success on statusBar
				ShowStatus("Finished Deployment");
			}
			catch (Exception e)
			{
				// Deployment failed - show error message
				string message = e.Message.ToString();
				errorList.Add(message);
				ShowStatus(message);
			}

			// Re-enable the ability to deploy again
			EnableDeployControls(true);
		}

		delegate void DeployDelegate(string sourceDir, string destinationDir, List<string> masks);
			
        private void ReplicateDirectory(string source, string destination)
        {
            // Loop through each source directory
            foreach (string subSource in Directory.GetDirectories(source))
            {
                // Create destination directory to mirror source directory
                string subDestination = subSource.Replace(source, destination);
                Directory.CreateDirectory(subDestination);

                // Output progress to statusBar
                ShowStatus("Created " + subDestination);
                
                // Drill down to the next level of directories and do it again
                ReplicateDirectory(subSource, subDestination);
            }
        }

        private void RecursiveFileCopy(string source, string destination, string filemask)
        {
            foreach (string file in Directory.GetFiles(source, filemask, SearchOption.AllDirectories))
            {
                FileInfo sourceFile = new FileInfo(file);
                FileInfo destinationFile = new FileInfo(file.Replace(source, destination));
				                
                // Check if directory exists
                if (!destinationFile.Directory.Exists)
					// If destination directory does not exist, create it now
                    Directory.CreateDirectory(destinationFile.Directory.FullName);

                // Copy the file
                System.IO.File.Copy(sourceFile.FullName, destinationFile.FullName, true);
               	copiedFiles.Add(destinationFile.FullName);

				// Output progress to statusBar
               	ShowStatus("Deploying " + destinationFile.FullName);
            }
        }

        private void SyncFormToProfiles()
        {
        	SyncFormToProfiles(0);
        }
        
		private void SyncFormToProfiles(int index)
		{
			// Add profiles to Drop Down
			profileComboBox.AllowDrop = true;
			profileComboBox.Items.Clear();
			foreach (Profile p in profiles)
			{
				profileComboBox.Items.Add(p.Name);
			}
				
			profileComboBox.SelectedIndex = index;
		}
		
		// Update the active profile with changes made in the form
		private void SyncProfileToForm()
		{
			int index = profileComboBox.SelectedIndex;

			// If free text entered in profileComboBox prompt to save profle
			// If there is an existing profile selected, updated that profile
			// with information from the form
			if (index == -1)
			{
				string message = "Add profile " + profileComboBox.Text + " before saving";
				throw new Exception(message);
			}
			else
			{
				Profile profile = profiles[index];

				// Update directories
				profile.SourceDirectory = sourceDirectoryTextBox.Text;
				profile.DestinationDirectory = destinationDirectoryTextBox.Text;

				// Update file masks
				profile.FileMasksFromListBox(fileMaskCheckedListBox);
			}
		}
		
        private void saveProfileButton_Click(object sender, EventArgs e)
        {
			try
			{
				// Update active profile with info from active form
				SyncProfileToForm();

            	WriteProfiles(activeProfilesFile);

				MessageBox.Show(profileComboBox.Text + " has been saved", "Save profile");
			}
			catch (Exception x)
			{
				string error = x.Message.ToString();
				//MessageBox.Show("Profiles could not be saved", "Error");
				MessageBox.Show(error, "Error");
			}
        }

        private void aboutRelaseItToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox aboutBox = new AboutBox();

            aboutBox.Show();
        }

        private void sourceDirectoryButton_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog folderDialog = new FolderBrowserDialog();

            folderDialog.ShowDialog();

            sourceDirectoryTextBox.Text = folderDialog.SelectedPath;
        }

        private void sourceDirectoryRadioButton_CheckedChanged(object sender, EventArgs e)
        {
            sourceDirectoryTextBox.Enabled = true;
            sourceDirectoryButton.Enabled = true;

            projectComboBox.Enabled = false;
            sourceProjectButton.Enabled = false;
            sourceConfigTextBox.Enabled = false;
        }

        private void sourceProjectRadioButton_CheckedChanged(object sender, EventArgs e)
        {
            projectComboBox.Enabled = true;
            sourceProjectButton.Enabled = true;
            sourceConfigTextBox.Enabled = true;

            sourceDirectoryTextBox.Enabled = false;
            sourceDirectoryButton.Enabled = false;
        }

        private void sourceProjectButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog openDialog = new OpenFileDialog();

            openDialog.Filter = "C# Projects (*.csproj)|*.csproj|Visual Basic Projects (*.vbproj)|*.vbproj";

            openDialog.ShowDialog();

            projectComboBox.Text = openDialog.FileName;
        }

        private void singleProfileDeploymentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            profileTabControl.SelectedTab = tabPage1;
        }

        private void mulitProfileDeploymentToolStripMenuItem_Click(object sender, EventArgs e)
        {
            profileTabControl.SelectedTab = tabPage2;
        }

        private void destinationDirectoryButton_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog folderDialog = new FolderBrowserDialog();

            folderDialog.ShowDialog();

            destinationDirectoryTextBox.Text = folderDialog.SelectedPath;
        }

        private void deployToolStripMenuItem_Click(object sender, EventArgs e)
        {
            deployButton_Click(sender, e);
        }

        private void copiedFilesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            CopiedFilesForm copiedForm = new CopiedFilesForm();

            foreach (string filename in copiedFiles)
            {
                copiedForm.CopiedListBox.Items.Add(filename);
            }
            copiedForm.Show();
        }

        private void newMaskButton_Click(object sender, EventArgs e)
        {
        	newFileMaskHandler(sender, e);
        }
        
        void AddMaskLinkLabelLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
        	newFileMaskHandler(sender, e);
        }
        
        private void newFileMaskHandler(object sender, EventArgs e)
        {
        	AddFileMaskForm dialog = new AddFileMaskForm();

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                // If a new mask was provided add it to the list
                string newMask = dialog.FileMask;
                if (newMask.Length > 0)
                {
                    // Add the new mask to the list
                    int index = fileMaskCheckedListBox.Items.Add(newMask);

                    // Set the new list item (new mask) to checked
                    fileMaskCheckedListBox.SetItemChecked(index, true);
                    
                    // Change the active profile to match the GUI
                    Profile activeProfile = profiles[profileComboBox.SelectedIndex];
                    activeProfile.FileMasksFromListBox(fileMaskCheckedListBox);
                }
            }
        }

        private void saveProfilesAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveDialog = new SaveFileDialog();

            saveDialog.Filter = "ReleaseIt Profiles (*.riprofiles)|*.riprofiles";
            saveDialog.ShowDialog();

            string filename = saveDialog.FileName;

            if (filename != "")
            	WriteProfiles(saveDialog.FileName);
        }

        private void loadProfilesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();

			dialog.Filter = "ReleaseIt Profiles (*.riprofiles)|*.riprofiles|UnLeashIt Profiles (*.uiprofiles)|*.uiprofiles";
			dialog.ShowDialog();

			string filename = dialog.FileName;

			if (filename != "")
			{
				try
				{
					LoadProfiles(filename);
					activeProfilesFile = filename;
				}
				catch (Exception x)
				{
					string message = x.Message.ToString();
					MessageBox.Show(message, "Error");
				}
			}

			SyncFormToProfiles();
        }

        private void addProfileButton_Click(object sender, EventArgs e)
        {
			// Create a new profile
			Profile newProfile = new Profile();

			// See if profileComboBox.Text should be the name
			if (profileComboBox.SelectedIndex == -1)
				newProfile.Name = profileComboBox.Text;

			// Add new profile to list of profiles
			profiles.Add(newProfile);

			// Add new profile to the form
			int index = profileComboBox.Items.Add(newProfile.Name);

			// Set the new profile as the active profile
			profileComboBox.SelectedIndex = index;
        }

        private void profileComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
        	//SyncActiveProfileToGui();
			SwitchProfile(profileComboBox.SelectedIndex);
        }
		
		private void SwitchProfile(int index)
		{
			Profile profile = profiles[index];
			
			//SyncFormToProfiles(index);
			
			sourceDirectoryTextBox.Text = profile.SourceDirectory;
			destinationDirectoryTextBox.Text = profile.DestinationDirectory;

			fileMaskCheckedListBox.Items.Clear();
			
			foreach (Mask m in profile.FileMasks)
			{
				int i = fileMaskCheckedListBox.Items.Add(m.Text);
				fileMaskCheckedListBox.SetItemChecked(i, m.IsSelected);
			}
		}
        
        void EditProfileButtonClick(object sender, EventArgs e)
        {
        	int index = profileComboBox.SelectedIndex;
        	Profile activeProfile = profiles[index];
        	
        	EditProfileForm dialog = new EditProfileForm();
        	
        	TextBox profileNameBox = dialog.ProfileName;

        	
        	profileNameBox.Text = activeProfile.Name;
        	        	
        	DialogResult result = dialog.ShowDialog();
        	
        	if (result == DialogResult.OK)
        	{
        		activeProfile.Name = profileNameBox.Text;
        		SyncFormToProfiles();
        		profileComboBox.SelectedIndex = index;
        	}
        }
        
        void RemoveMaskLinkLabelLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
        	// Find out which file mask is to be removed
        	int maskIndex = fileMaskCheckedListBox.SelectedIndex;
        	 	
        	// Remove the selected mask from the GUI
        	fileMaskCheckedListBox.Items.RemoveAt(maskIndex);
        	
        	// Update the in-memory profile
        	Profile activeProfile = profiles[profileComboBox.SelectedIndex];
        	activeProfile.FileMasksFromListBox(fileMaskCheckedListBox);
        }
        
        void SourceDirectoryTextBoxTextChanged(object sender, EventArgs e)
        {
        	// Update the in-memory profile
        	Profile activeProfile = profiles[profileComboBox.SelectedIndex];
        	
        	activeProfile.SourceDirectory = sourceDirectoryTextBox.Text;
        }
        
        void DestinationDirectoryTextBoxTextChanged(object sender, EventArgs e)
        {
        	// Update the in-memory profile
        	Profile activeProfile = profiles[profileComboBox.SelectedIndex];
        	
        	activeProfile.DestinationDirectory = destinationDirectoryTextBox.Text;
        }
    }
}
